#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
凯利公式计算器启动器 - 改进版
功能说明：用于启动Streamlit凯利公式计算器的启动器脚本，支持exe打包，内嵌所有依赖文件
作者：luohe
创建时间：2024-12-19
修改时间：2024-12-19
"""

import sys
import os
import subprocess
import tkinter as tk
from tkinter import messagebox, ttk
import threading
import webbrowser
import time
from pathlib import Path
import tempfile
import shutil
from typing import Optional
import socket
try:
    import psutil
except ImportError:
    psutil = None

class KellyCalculatorLauncher:
    """凯利公式计算器启动器 - 改进版"""
    
    # 内嵌的requirements.txt内容
    REQUIREMENTS_CONTENT = """# 基础数据处理和科学计算
numpy>=1.21.0
pandas>=1.3.0
scipy>=1.7.0

# 可视化库
matplotlib>=3.5.0
seaborn>=0.11.0
plotly>=5.0.0

# Streamlit Web应用框架
streamlit>=1.28.0

# Excel文件处理
openpyxl>=3.0.9
xlsxwriter>=3.0.0

# 打包工具（可选）
pyinstaller>=5.0.0

# 其他实用工具
requests>=2.25.0
psutil>=5.8.0
"""
    
    # 内嵌的完整streamlit应用内容
    STREAMLIT_APP_CONTENT = '''#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Streamlit凯利公式计算器
功能说明：基于Streamlit的交互式凯利公式计算器
作者：luohe
创建时间：2024-12-19
修改时间：2024-12-19
"""

import streamlit as st
import numpy as np
import pandas as pd
from dataclasses import dataclass
from typing import Tuple, Optional
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import io
# Excel导出功能已移除以避免依赖问题
import random

# 配置页面
st.set_page_config(
    page_title="凯利公式计算器",
    page_icon="📊",
    layout="wide",
    initial_sidebar_state="expanded"
)

# 初始化session state
if \'calculation_history\' not in st.session_state:
    st.session_state.calculation_history = []

@dataclass
class KellyResult:
    """凯利公式计算结果"""
    optimal_fraction: float
    expected_growth: float
    conservative_fraction: float
    aggressive_fraction: float
    risk_level: str
    recommendation: str

class StreamlitKellyCalculator:
    """Streamlit版本的凯利公式计算器"""
    
    def __init__(self):
        self.risk_thresholds = {
            \'low\': 0.1,
            \'medium\': 0.25,
            \'high\': 0.5
        }
    
    def validate_inputs(self, win_rate: float, win_return: float, loss_rate: float) -> Tuple[bool, str]:
        """验证输入参数"""
        if not (0 < win_rate < 1):
            return False, "胜率必须在0到1之间"
        
        if win_return <= 0:
            return False, "胜利收益率必须大于0"
        
        if loss_rate <= 0:
            return False, "失败损失率必须大于0"
        
        if loss_rate > 1:
            return False, "失败损失率不能大于1（会导致破产）"
        
        return True, ""
    
    def calculate_kelly_fraction(self, win_rate: float, win_return: float, loss_rate: float) -> KellyResult:
        """计算凯利公式"""
        # 凯利公式: f* = (bp - q) / b
        # 其中 b = 盈亏比, p = 胜率, q = 败率
        
        lose_rate = 1 - win_rate
        odds_ratio = win_return / loss_rate
        
        # 计算最优投资比例
        optimal_fraction = (odds_ratio * win_rate - lose_rate) / odds_ratio
        
        # 确保不为负数
        optimal_fraction = max(0, optimal_fraction)
        
        # 计算期望增长率
        if optimal_fraction > 0:
            expected_growth = win_rate * np.log(1 + optimal_fraction * win_return) + lose_rate * np.log(1 - optimal_fraction * loss_rate)
        else:
            expected_growth = 0
        
        # 计算保守和激进策略
        conservative_fraction = optimal_fraction * 0.5
        aggressive_fraction = optimal_fraction * 0.75
        
        # 评估风险等级
        risk_level = self._assess_risk_level(optimal_fraction)
        
        # 生成建议
        recommendation = self._generate_recommendation(optimal_fraction, risk_level)
        
        return KellyResult(
            optimal_fraction=optimal_fraction,
            expected_growth=expected_growth,
            conservative_fraction=conservative_fraction,
            aggressive_fraction=aggressive_fraction,
            risk_level=risk_level,
            recommendation=recommendation
        )
    
    def _assess_risk_level(self, fraction: float) -> str:
        """评估风险等级"""
        if fraction <= self.risk_thresholds[\'low\']:
            return "低风险"
        elif fraction <= self.risk_thresholds[\'medium\']:
            return "中等风险"
        elif fraction <= self.risk_thresholds[\'high\']:
            return "高风险"
        else:
            return "极高风险"
    
    def _generate_recommendation(self, fraction: float, risk_level: str) -> str:
        """生成投资建议"""
        if fraction <= 0:
            return "当前参数下不建议投资，期望收益为负"
        elif fraction <= 0.05:
            return "建议小额投资，严格控制风险"
        elif fraction <= 0.15:
            return "可以适度投资，注意分散风险"
        elif fraction <= 0.3:
            return "建议谨慎投资，不要使用全部资金"
        else:
            return "风险极高，建议降低投资比例或重新评估策略"
    
    def calculate_growth_curve(self, win_rate: float, win_return: float, loss_rate: float) -> Tuple[list, list]:
        """计算不同投资比例下的增长率曲线"""
        fractions = np.linspace(0, 1, 101)
        growth_rates = []
        
        lose_rate = 1 - win_rate
        
        for f in fractions:
            if f == 0:
                growth_rate = 0
            else:
                # 检查是否会导致破产
                if f * loss_rate >= 1:
                    growth_rate = -np.inf
                else:
                    growth_rate = win_rate * np.log(1 + f * win_return) + lose_rate * np.log(1 - f * loss_rate)
            
            growth_rates.append(growth_rate)
        
        return fractions.tolist(), growth_rates
    
    def plot_growth_curve(self, win_rate: float, win_return: float, loss_rate: float, kelly_result: KellyResult):
        """绘制增长率曲线"""
        fractions, growth_rates = self.calculate_growth_curve(win_rate, win_return, loss_rate)
        
        fig = go.Figure()
        
        # 主曲线
        fig.add_trace(go.Scatter(
            x=[f * 100 for f in fractions],
            y=[g * 100 for g in growth_rates],
            mode=\'lines\',
            name=\'期望增长率\',
            line=dict(color=\'blue\', width=2)
        ))
        
        # 标记最优点
        fig.add_trace(go.Scatter(
            x=[kelly_result.optimal_fraction * 100],
            y=[kelly_result.expected_growth * 100],
            mode=\'markers\',
            name=\'理论最优\',
            marker=dict(color=\'red\', size=10, symbol=\'star\')
        ))
        
        # 标记保守策略
        conservative_growth = win_rate * np.log(1 + kelly_result.conservative_fraction * win_return) + (1 - win_rate) * np.log(1 - kelly_result.conservative_fraction * loss_rate)
        
        fig.add_trace(go.Scatter(
            x=[kelly_result.conservative_fraction * 100],
            y=[conservative_growth * 100],
            mode=\'markers\',
            name=\'保守策略\',
            marker=dict(color=\'green\', size=8, symbol=\'circle\')
        ))
        
        # 标记激进策略
        aggressive_growth = win_rate * np.log(1 + kelly_result.aggressive_fraction * win_return) + (1 - win_rate) * np.log(1 - kelly_result.aggressive_fraction * loss_rate)
        
        fig.add_trace(go.Scatter(
            x=[kelly_result.aggressive_fraction * 100],
            y=[aggressive_growth * 100],
            mode=\'markers\',
            name=\'激进策略\',
            marker=dict(color=\'orange\', size=8, symbol=\'diamond\')
        ))
        
        fig.update_layout(
            title=\'不同投资比例下的期望增长率\',
            xaxis_title=\'投资比例 (%)\',
            yaxis_title=\'期望增长率 (%)\',
            hovermode=\'x unified\'
        )
        
        return fig
    
    def plot_risk_return_analysis(self, win_rate: float, win_return: float, loss_rate: float, kelly_result: KellyResult):
        """绘制风险收益分析图"""
        fractions = np.linspace(0, min(0.8, kelly_result.optimal_fraction * 2), 50)
        expected_returns = []
        volatilities = []
        
        lose_rate = 1 - win_rate
        
        for f in fractions:
            if f == 0:
                expected_return = 0
                volatility = 0
            else:
                # 期望收益
                expected_return = win_rate * f * win_return - lose_rate * f * loss_rate
                
                # 波动率（标准差）
                win_outcome = f * win_return
                lose_outcome = -f * loss_rate
                expected_outcome = win_rate * win_outcome + lose_rate * lose_outcome
                
                variance = win_rate * (win_outcome - expected_outcome)**2 + lose_rate * (lose_outcome - expected_outcome)**2
                volatility = np.sqrt(variance)
            
            expected_returns.append(expected_return * 100)
            volatilities.append(volatility * 100)
        
        fig = go.Figure()
        
        # 效率前沿
        fig.add_trace(go.Scatter(
            x=volatilities,
            y=expected_returns,
            mode=\'lines+markers\',
            name=\'效率前沿\',
            line=dict(color=\'blue\', width=2),
            marker=dict(size=4)
        ))
        
        # 标记关键点
        strategies = {
            \'保守策略\': kelly_result.conservative_fraction,
            \'激进策略\': kelly_result.aggressive_fraction,
            \'理论最优\': kelly_result.optimal_fraction
        }
        
        colors = [\'green\', \'orange\', \'red\']
        symbols = [\'circle\', \'diamond\', \'star\']
        
        for i, (name, fraction) in enumerate(strategies.items()):
            if fraction > 0:
                expected_return = win_rate * fraction * win_return - lose_rate * fraction * loss_rate
                
                win_outcome = fraction * win_return
                lose_outcome = -fraction * loss_rate
                expected_outcome = win_rate * win_outcome + lose_rate * lose_outcome
                
                variance = win_rate * (win_outcome - expected_outcome)**2 + lose_rate * (lose_outcome - expected_outcome)**2
                volatility = np.sqrt(variance)
                
                fig.add_trace(go.Scatter(
                    x=[volatility * 100],
                    y=[expected_return * 100],
                    mode=\'markers\',
                    name=name,
                    marker=dict(color=colors[i], size=12, symbol=symbols[i])
                ))
        
        fig.update_layout(
            title=\'风险收益分析\',
            xaxis_title=\'风险 (波动率 %)\',
            yaxis_title=\'期望收益率 (%)\',
            hovermode=\'closest\'
        )
        
        return fig
    
    def create_strategy_comparison(self, kelly_result: KellyResult):
        """创建策略对比雷达图"""
        categories = [\'收益潜力\', \'风险控制\', \'稳定性\', \'实用性\', \'推荐度\']
        
        # 不同策略的评分（1-5分）
        conservative_scores = [2, 5, 5, 5, 4]
        aggressive_scores = [4, 3, 3, 4, 3]
        optimal_scores = [5, 2, 2, 2, 2]
        
        fig = go.Figure()
        
        fig.add_trace(go.Scatterpolar(
            r=conservative_scores,
            theta=categories,
            fill=\'toself\',
            name=\'保守策略\',
            line_color=\'green\'
        ))
        
        fig.add_trace(go.Scatterpolar(
            r=aggressive_scores,
            theta=categories,
            fill=\'toself\',
            name=\'激进策略\',
            line_color=\'orange\'
        ))
        
        fig.add_trace(go.Scatterpolar(
            r=optimal_scores,
            theta=categories,
            fill=\'toself\',
            name=\'理论最优\',
            line_color=\'red\'
        ))
        
        fig.update_layout(
            polar=dict(
                radialaxis=dict(
                    visible=True,
                    range=[0, 5]
                )
            ),
            title="投资策略对比分析",
            showlegend=True
        )
        
        return fig
    
    # Excel导出功能已移除以避免依赖问题
    
    def run_simulation(self, win_rate: float, win_return: float, loss_rate: float, 
                      fraction: float, initial_capital: float, trades: int) -> list:
        """运行投资模拟"""
        try:
            capital_history = [initial_capital]
            current_capital = initial_capital
            
            # 生成随机结果 - 每次使用不同的随机种子以获得不同的模拟结果
            import time
            random.seed(int(time.time() * 1000000) % 2147483647)  # 使用当前时间戳作为种子
            random_outcomes = [random.random() for _ in range(trades)]
            
            for i in range(trades):
                if current_capital <= 0:
                    capital_history.append(0)
                    continue
                
                bet_amount = current_capital * fraction
                
                if random_outcomes[i] < win_rate:
                    # 胜利
                    current_capital += bet_amount * win_return
                else:
                    # 失败
                    current_capital = max(0, current_capital - bet_amount * loss_rate)
                
                capital_history.append(current_capital)
            
            return capital_history
            
        except Exception as e:
            st.error(f"模拟过程中发生错误: {str(e)}")
            return [initial_capital] * (trades + 1)  # 返回平稳的资金线

def main():
    """主函数"""
    st.title("📊 凯利公式计算器")
    st.markdown("---")
    
    calculator = StreamlitKellyCalculator()
    
    # 侧边栏输入
    with st.sidebar:
        st.header("📝 参数设置")
        
        # 输入参数
        st.subheader("📊 核心参数")
        
        win_rate_percent = st.number_input(
            "胜率 (%)", 
            min_value=0.1, 
            max_value=99.9, 
            value=35.0, 
            step=0.1,
            format="%.1f",
            help="交易获胜的概率百分比"
        )
        win_rate = win_rate_percent / 100
        
        win_return_percent = st.number_input(
            "胜利收益率 (%)", 
            min_value=0.1, 
            max_value=1000.0, 
            value=200.0, 
            step=0.1,
            format="%.1f",
            help="交易获胜时的收益率百分比"
        )
        win_return = win_return_percent / 100
        
        loss_rate_percent = st.number_input(
            "失败损失率 (%)", 
            min_value=0.1, 
            max_value=100.0, 
            value=100.0, 
            step=0.1,
            format="%.1f",
            help="交易失败时的损失率百分比"
        )
        loss_rate = loss_rate_percent / 100
        
        # 显示计算的盈亏比
        st.info(f"💡 当前盈亏比：{win_return/loss_rate:.2f}:1")
        
        st.markdown("---")
        
        # 模拟参数
        st.subheader("🎲 模拟设置")
        initial_capital = st.number_input("初始资金", 1000, 1000000, 10000, 1000)
        simulation_trades = st.number_input(
            "模拟交易次数", 
            min_value=10, 
            max_value=10000, 
            value=100, 
            step=10,
            help="设置模拟交易的次数，用于测试不同策略的表现"
        )
        
        # 计算按钮
        calculate_button = st.button("🚀 开始计算", type="primary")
    
    # 主要内容区域
    if calculate_button:
        # 验证输入
        is_valid, error_msg = calculator.validate_inputs(win_rate, win_return, loss_rate)
        
        if not is_valid:
            st.error(f"❌ 输入错误：{error_msg}")
            return
        
        # 计算凯利结果
        kelly_result = calculator.calculate_kelly_fraction(win_rate, win_return, loss_rate)
        
        # 保存到历史记录
        st.session_state.calculation_history.append({
            \'时间\': pd.Timestamp.now().strftime(\'%Y-%m-%d %H:%M:%S\'),
            \'胜率\': f"{win_rate:.1%}",
            \'胜利收益率\': f"{win_return:.1%}",
            \'失败损失率\': f"{loss_rate:.1%}",
            \'最优比例\': f"{kelly_result.optimal_fraction:.2%}",
            \'风险等级\': kelly_result.risk_level
        })
        
        # 显示核心结果
        st.header("🎯 计算结果")
        
        col1, col2, col3, col4 = st.columns(4)
        
        with col1:
            st.metric(
                "理论最优投资比例",
                f"{kelly_result.optimal_fraction:.2%}",
                help="根据凯利公式计算的理论最优投资比例"
            )
        
        with col2:
            st.metric(
                "期望增长率",
                f"{kelly_result.expected_growth:.2%}",
                help="使用最优比例时的期望增长率"
            )
        
        with col3:
            st.metric(
                "风险等级",
                kelly_result.risk_level,
                help="基于投资比例评估的风险等级"
            )
        
        with col4:
            st.metric(
                "盈亏比",
                f"{win_return/loss_rate:.2f}:1",
                help="胜利收益率与失败损失率的比值"
            )
        
        # 投资建议
        st.subheader("💡 投资建议")
        
        col1, col2 = st.columns(2)
        
        with col1:
            st.info(f"**保守策略：** {kelly_result.conservative_fraction:.2%} (50%凯利)")
            st.success(f"**激进策略：** {kelly_result.aggressive_fraction:.2%} (75%凯利)")
        
        with col2:
            st.warning(f"**专业建议：** {kelly_result.recommendation}")
        
        # 导出功能已移除，直接进入可视化分析
        
        # 可视化分析
        st.header("📈 可视化分析")
        
        tab1, tab2, tab3, tab4 = st.tabs(["增长率曲线", "风险收益分析", "策略对比", "模拟测试"])
        
        with tab1:
            st.plotly_chart(calculator.plot_growth_curve(win_rate, win_return, loss_rate, kelly_result), use_container_width=True)
        
        with tab2:
            st.plotly_chart(calculator.plot_risk_return_analysis(win_rate, win_return, loss_rate, kelly_result), use_container_width=True)
        
        with tab3:
            st.plotly_chart(calculator.create_strategy_comparison(kelly_result), use_container_width=True)
        
        with tab4:
            st.subheader("🎲 投资策略模拟")
            
            # 模拟不同策略
            strategies = {
                \'保守策略\': kelly_result.conservative_fraction,
                \'激进策略\': kelly_result.aggressive_fraction,
                \'理论最优\': kelly_result.optimal_fraction
            }
            
            simulation_results = {}
            
            for name, fraction in strategies.items():
                if fraction > 0:
                    capital_history = calculator.run_simulation(
                        win_rate, win_return, loss_rate, fraction, initial_capital, simulation_trades
                    )
                    simulation_results[name] = capital_history
            
            # 绘制模拟结果
            if simulation_results:
                fig = go.Figure()
                
                colors = [\'green\', \'orange\', \'red\']
                for i, (name, history) in enumerate(simulation_results.items()):
                    fig.add_trace(go.Scatter(
                        x=list(range(len(history))),
                        y=history,
                        mode=\'lines\',
                        name=name,
                        line=dict(color=colors[i % len(colors)], width=2)
                    ))
                
                fig.update_layout(
                    title=f"资金曲线模拟 ({simulation_trades}次交易)",
                    xaxis_title="交易次数",
                    yaxis_title="资金金额",
                    hovermode=\'x unified\'
                )
                
                st.plotly_chart(fig, use_container_width=True)
                
                # 模拟结果统计
                st.subheader("📊 模拟结果统计")
                
                results_df = pd.DataFrame({
                    \'策略\': list(simulation_results.keys()),
                    \'最终资金\': [history[-1] for history in simulation_results.values()],
                    \'总收益率\': [(history[-1] / initial_capital - 1) * 100 for history in simulation_results.values()],
                    \'最大资金\': [max(history) for history in simulation_results.values()],
                    \'最小资金\': [min(history) for history in simulation_results.values()]
                })
                
                results_df[\'最终资金\'] = results_df[\'最终资金\'].apply(lambda x: f"{x:,.0f}")
                results_df[\'总收益率\'] = results_df[\'总收益率\'].apply(lambda x: f"{x:.1f}%")
                results_df[\'最大资金\'] = results_df[\'最大资金\'].apply(lambda x: f"{x:,.0f}")
                results_df[\'最小资金\'] = results_df[\'最小资金\'].apply(lambda x: f"{x:,.0f}")
                
                st.dataframe(results_df, use_container_width=True)
    
    # 历史记录
    if st.session_state.calculation_history:
        st.header("📋 计算历史")
        history_df = pd.DataFrame(st.session_state.calculation_history)
        st.dataframe(history_df, use_container_width=True)
        
        if st.button("🗑️ 清空历史记录"):
            st.session_state.calculation_history = []
            st.rerun()
    
    # 帮助信息
    with st.expander("❓ 使用说明"):
        st.markdown("""
        ### 凯利公式说明
        
        凯利公式是一个用于确定最优投资比例的数学公式：
        
        **f* = (bp - q) / b**
        
        其中：
        - f* = 最优投资比例
        - b = 盈亏比 (胜利收益率 / 失败损失率)
        - p = 胜率
        - q = 败率 (1 - 胜率)
        
        ### 使用建议
        
        1. **保守策略**：使用理论最优值的50%
        2. **激进策略**：使用理论最优值的75%
        3. **风险控制**：避免使用100%的理论最优值
        4. **动态调整**：根据市场变化及时调整参数
        
        ### 注意事项
        
        - 凯利公式基于理论假设，实际应用需要考虑更多因素
        - 高投资比例虽然理论收益高，但破产风险也相应增加
        - 建议结合实际情况和风险承受能力进行调整
        """)

if __name__ == "__main__":
    main()
'''
    
    def __init__(self):
        """初始化启动器"""
        self.root = tk.Tk()
        self.root.title("凯利公式计算器启动器")
        self.root.geometry("500x400")
        self.root.resizable(False, False)
        
        # 设置图标（如果有的话）
        try:
            self.root.iconbitmap(default="calculator.ico")
        except:
            pass
        
        self.streamlit_process: Optional[subprocess.Popen] = None
        self.server_url = "http://localhost:8501"
        self.temp_dir: Optional[str] = None
        
        self.setup_ui()
        self.create_temp_files()
    
    def setup_ui(self):
        """设置用户界面"""
        # 主标题
        title_frame = tk.Frame(self.root, bg="#f0f0f0", height=80)
        title_frame.pack(fill="x", padx=10, pady=10)
        title_frame.pack_propagate(False)
        
        title_label = tk.Label(
            title_frame, 
            text="📊 凯利公式计算器", 
            font=("Arial", 18, "bold"),
            bg="#f0f0f0",
            fg="#2c3e50"
        )
        title_label.pack(expand=True)
        
        subtitle_label = tk.Label(
            title_frame,
            text="专业的投资比例计算与风险分析工具 - 改进版",
            font=("Arial", 10),
            bg="#f0f0f0",
            fg="#7f8c8d"
        )
        subtitle_label.pack()
        
        # 状态显示区域
        status_frame = tk.LabelFrame(self.root, text="系统状态", padx=10, pady=10)
        status_frame.pack(fill="x", padx=10, pady=5)
        
        self.status_text = tk.Text(
            status_frame, 
            height=8, 
            width=60,
            font=("Consolas", 9),
            bg="#2c3e50",
            fg="#ecf0f1",
            insertbackground="#ecf0f1"
        )
        self.status_text.pack(fill="both", expand=True)
        
        # 滚动条
        scrollbar = tk.Scrollbar(status_frame)
        scrollbar.pack(side="right", fill="y")
        self.status_text.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.status_text.yview)
        
        # 进度条
        self.progress = ttk.Progressbar(
            self.root, 
            mode='indeterminate',
            length=480
        )
        self.progress.pack(padx=10, pady=5)
        
        # 按钮区域
        button_frame = tk.Frame(self.root)
        button_frame.pack(fill="x", padx=10, pady=10)
        
        # 启动按钮
        self.start_button = tk.Button(
            button_frame,
            text="🚀 启动计算器",
            font=("Arial", 12, "bold"),
            bg="#27ae60",
            fg="white",
            relief="flat",
            padx=20,
            pady=10,
            command=self.start_calculator
        )
        self.start_button.pack(side="left", padx=5)
        
        # 打开浏览器按钮
        self.browser_button = tk.Button(
            button_frame,
            text="🌐 打开浏览器",
            font=("Arial", 12),
            bg="#3498db",
            fg="white",
            relief="flat",
            padx=20,
            pady=10,
            command=self.open_browser,
            state="disabled"
        )
        self.browser_button.pack(side="left", padx=5)
        
        # 停止按钮
        self.stop_button = tk.Button(
            button_frame,
            text="⏹️ 停止服务",
            font=("Arial", 12),
            bg="#e74c3c",
            fg="white",
            relief="flat",
            padx=20,
            pady=10,
            command=self.stop_calculator,
            state="disabled"
        )
        self.stop_button.pack(side="left", padx=5)
        
        # 退出按钮
        exit_button = tk.Button(
            button_frame,
            text="❌ 退出",
            font=("Arial", 12),
            bg="#95a5a6",
            fg="white",
            relief="flat",
            padx=20,
            pady=10,
            command=self.exit_application
        )
        exit_button.pack(side="right", padx=5)
    
    def log_message(self, message: str, level: str = "INFO"):
        """记录日志消息"""
        timestamp = time.strftime("%H:%M:%S")
        log_entry = f"[{timestamp}] {level}: {message}\n"
        
        self.status_text.insert(tk.END, log_entry)
        self.status_text.see(tk.END)
        self.root.update()
    
    def create_temp_files(self):
        """创建临时文件"""
        try:
            # 创建临时目录
            self.temp_dir = tempfile.mkdtemp(prefix="kelly_calculator_")
            self.log_message(f"创建临时目录: {self.temp_dir}")
            
            # 创建streamlit应用文件
            app_file = os.path.join(self.temp_dir, "streamlit_kelly_calculator.py")
            with open(app_file, 'w', encoding='utf-8') as f:
                f.write(self.STREAMLIT_APP_CONTENT)
            
            # 创建requirements文件
            req_file = os.path.join(self.temp_dir, "requirements.txt")
            with open(req_file, 'w', encoding='utf-8') as f:
                f.write(self.REQUIREMENTS_CONTENT)
            
            self.log_message("✓ 临时文件创建成功")
            
        except Exception as e:
            self.log_message(f"❌ 创建临时文件失败: {str(e)}", "ERROR")
    
    def check_dependencies(self):
        """检查依赖包"""
        self.log_message("正在检查依赖包...")
        
        required_packages = [
            "streamlit",
            "plotly",
            "numpy",
            "pandas",
            "psutil"
        ]
        
    def find_available_port(self, start_port: int = 8501) -> int:
        """查找可用端口"""
        for port in range(start_port, start_port + 100):
            try:
                with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                    s.bind(('localhost', port))
                    return port
            except OSError:
                continue
        return start_port  # 如果都不可用，返回默认端口
    
    def kill_process_on_port(self, port: int) -> bool:
        """关闭占用指定端口的进程"""
        if not psutil:
            self.log_message("psutil未安装，无法检测端口占用", "WARNING")
            return False
            
        try:
            for proc in psutil.process_iter(['pid', 'name', 'connections']):
                try:
                    connections = proc.info['connections']
                    if connections:
                        for conn in connections:
                            if conn.laddr.port == port:
                                self.log_message(f"发现占用端口{port}的进程: {proc.info['name']} (PID: {proc.info['pid']})")
                                proc.terminate()
                                proc.wait(timeout=3)
                                self.log_message(f"已终止进程 PID: {proc.info['pid']}")
                                return True
                except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                    continue
        except Exception as e:
            self.log_message(f"检测端口占用时出错: {str(e)}", "ERROR")
        
        return False
        
        missing_packages = []
        
        for package in required_packages:
            try:
                __import__(package)
                self.log_message(f"✓ {package} 已安装")
            except ImportError:
                missing_packages.append(package)
                self.log_message(f"❌ {package} 未安装", "WARNING")
        
        # Excel导出功能已移除，无需xlsxwriter依赖
        
        if missing_packages:
            self.log_message("正在安装缺失的依赖包...", "INFO")
            self.install_dependencies(missing_packages)
            
            # 依赖包安装完成
            self.log_message("✓ 依赖包安装完成")
    
    def install_dependencies(self, packages):
        """安装依赖包"""
        try:
            # 检测是否在exe环境中运行
            if getattr(sys, 'frozen', False):
                # 在exe环境中，尝试多种Python路径
                import shutil
                python_cmd = shutil.which('python') or shutil.which('python3') or 'python'
                if not python_cmd or python_cmd == 'python':
                    # 如果找不到python，尝试使用sys.executable的目录
                    import os
                    exe_dir = os.path.dirname(sys.executable)
                    possible_paths = [
                        os.path.join(exe_dir, 'python.exe'),
                        os.path.join(exe_dir, 'Scripts', 'python.exe'),
                        'python'
                    ]
                    for path in possible_paths:
                        if os.path.exists(path) or path == 'python':
                            python_cmd = path
                            break
            else:
                python_cmd = sys.executable
                
            for package in packages:
                self.log_message(f"正在安装 {package}...")
                
                # 隐藏控制台窗口的subprocess调用
                startupinfo = None
                if sys.platform == "win32":
                    startupinfo = subprocess.STARTUPINFO()
                    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                    startupinfo.wShowWindow = subprocess.SW_HIDE
                
                # 尝试多种安装方式
                install_commands = [
                    [python_cmd, "-m", "pip", "install", package],
                    ["pip", "install", package],
                    ["python", "-m", "pip", "install", package]
                ]
                
                result = None
                for cmd in install_commands:
                    try:
                        result = subprocess.run(
                            cmd,
                            capture_output=True,
                            text=True,
                            timeout=300,
                            startupinfo=startupinfo
                        )
                        if result.returncode == 0:
                            break
                    except (FileNotFoundError, subprocess.TimeoutExpired):
                        continue
                
                if result and result.returncode == 0:
                    self.log_message(f"✓ {package} 安装成功")
                else:
                    error_msg = result.stderr if result else "所有安装方式都失败"
                    self.log_message(f"❌ {package} 安装失败: {error_msg}", "ERROR")
        
        except subprocess.TimeoutExpired:
            self.log_message("安装超时，请检查网络连接", "ERROR")
        except Exception as e:
            self.log_message(f"安装过程出错: {str(e)}", "ERROR")
    
    def start_calculator(self):
        """启动计算器"""
        if self.streamlit_process and self.streamlit_process.poll() is None:
            self.log_message("计算器已在运行中", "WARNING")
            return
        
        self.log_message("正在启动凯利公式计算器...")
        self.progress.start()
        
        # 检查端口占用
        port = 8501
        self.log_message(f"检查端口 {port} 是否可用...")
        
        # 尝试连接端口检查是否被占用
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.settimeout(1)
                result = s.connect_ex(('localhost', port))
                if result == 0:
                    self.log_message(f"端口 {port} 被占用，尝试关闭占用进程...")
                    if self.kill_process_on_port(port):
                        time.sleep(2)  # 等待进程完全关闭
                        self.log_message(f"端口 {port} 已释放")
                    else:
                        # 如果无法关闭进程，查找可用端口
                        available_port = self.find_available_port(port + 1)
                        if available_port != port:
                            self.log_message(f"使用替代端口: {available_port}")
                            self.server_url = f"http://localhost:{available_port}"
                            port = available_port
                else:
                    self.log_message(f"端口 {port} 可用")
        except Exception as e:
            self.log_message(f"端口检查出错: {str(e)}", "WARNING")
        
        # 检查依赖
        self.check_dependencies()
        
        # 在新线程中启动
        thread = threading.Thread(target=self._start_streamlit, args=(port,))
        thread.daemon = True
        thread.start()
    
    def _start_streamlit(self, port: int = 8501):
        """在后台启动Streamlit"""
        try:
            if not self.temp_dir:
                self.log_message("❌ 临时目录不存在", "ERROR")
                self.root.after(0, self._update_buttons_after_error)
                return
            
            # 检测是否在exe环境中运行
            if getattr(sys, 'frozen', False):
                python_cmd = "python"
            else:
                python_cmd = sys.executable
            
            app_file = os.path.join(self.temp_dir, "streamlit_kelly_calculator.py")
            
            # 隐藏控制台窗口的subprocess调用
            startupinfo = None
            if sys.platform == "win32":
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                startupinfo.wShowWindow = subprocess.SW_HIDE
            
            # 启动Streamlit应用
            self.streamlit_process = subprocess.Popen(
                [python_cmd, "-m", "streamlit", "run", app_file, 
                 f"--server.port={port}", "--server.headless=true"],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                startupinfo=startupinfo,
                cwd=self.temp_dir
            )
            
            # 等待服务启动
            time.sleep(3)
            
            if self.streamlit_process.poll() is None:
                self.log_message("✓ 计算器启动成功！")
                self.log_message(f"访问地址: {self.server_url}")
                
                # 更新按钮状态
                self.root.after(0, self._update_buttons_after_start)
                
                # 自动打开浏览器
                time.sleep(1)
                self.root.after(0, self.open_browser)
                
                # 监控进程输出
                self._monitor_process_output()
            else:
                stderr_output = self.streamlit_process.stderr.read() if self.streamlit_process.stderr else "未知错误"
                self.log_message(f"❌ 计算器启动失败: {stderr_output}", "ERROR")
                self.root.after(0, self._update_buttons_after_error)
        
        except Exception as e:
            self.log_message(f"启动过程出错: {str(e)}", "ERROR")
            self.root.after(0, self._update_buttons_after_error)
        
        finally:
            self.root.after(0, lambda: self.progress.stop())
    
    def _monitor_process_output(self):
        """监控进程输出并显示在界面中"""
        def read_output():
            if self.streamlit_process and self.streamlit_process.stdout:
                try:
                    while True:
                        line = self.streamlit_process.stdout.readline()
                        if not line:
                            break
                        if line.strip():
                            self.root.after(0, lambda msg=line.strip(): self.log_message(f"Streamlit: {msg}"))
                except:
                    pass
        
        thread = threading.Thread(target=read_output)
        thread.daemon = True
        thread.start()
    
    def _update_buttons_after_start(self):
        """启动成功后更新按钮状态"""
        self.start_button.config(state="disabled")
        self.browser_button.config(state="normal")
        self.stop_button.config(state="normal")
    
    def _update_buttons_after_error(self):
        """启动失败后更新按钮状态"""
        self.start_button.config(state="normal")
        self.browser_button.config(state="disabled")
        self.stop_button.config(state="disabled")
    
    def open_browser(self):
        """打开浏览器"""
        try:
            webbrowser.open(self.server_url)
            self.log_message(f"已打开浏览器: {self.server_url}")
        except Exception as e:
            self.log_message(f"打开浏览器失败: {str(e)}", "ERROR")
    
    def stop_calculator(self):
        """停止计算器"""
        if self.streamlit_process:
            try:
                self.streamlit_process.terminate()
                self.streamlit_process.wait(timeout=5)
                self.log_message("✓ 计算器已停止")
            except subprocess.TimeoutExpired:
                self.streamlit_process.kill()
                self.log_message("强制停止计算器")
            except Exception as e:
                self.log_message(f"停止过程出错: {str(e)}", "ERROR")
            
            self.streamlit_process = None
        
        # 更新按钮状态
        self.start_button.config(state="normal")
        self.browser_button.config(state="disabled")
        self.stop_button.config(state="disabled")
    
    def cleanup_temp_files(self):
        """清理临时文件"""
        if self.temp_dir and os.path.exists(self.temp_dir):
            try:
                shutil.rmtree(self.temp_dir)
                self.log_message("✓ 临时文件已清理")
            except Exception as e:
                self.log_message(f"清理临时文件失败: {str(e)}", "WARNING")
    
    def exit_application(self):
        """退出应用"""
        if self.streamlit_process:
            self.stop_calculator()
        
        self.cleanup_temp_files()
        self.root.quit()
        self.root.destroy()
    
    def run(self):
        """运行启动器"""
        # 设置关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.exit_application)
        
        self.log_message("凯利公式计算器启动器已就绪")
        self.log_message("所有依赖文件已内嵌，无需外部文件")
        
        # 启动主循环
        self.root.mainloop()

def main():
    """主函数"""
    try:
        launcher = KellyCalculatorLauncher()
        launcher.run()
    except Exception as e:
        messagebox.showerror("启动错误", f"启动器运行出错:\n{str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    main()